/*
 * May 29, 2005
 */
package traer.physics;

/**
 * @author Jeffrey Traer Bernstein, modifications by Roland Haring <P>
 * <a href="http://www.cs.princeton.edu/~traer/physics/" target="blank">http://www.cs.princeton.edu/~traer/physics/</a>
 */
public class Spring implements Force {

    float springConstant;
    float damping;
    float restLength;
    Particle a, b;
    boolean on;

    public Spring(Particle A, Particle B, float springConstant, float damping, float restLength) {
        this.springConstant = springConstant;
        this.damping = damping;
        this.restLength = restLength;
        a = A;
        b = B;
        on = true;
    }

	public final void apply() {
        if (on && (a.isFree() || b.isFree())) {
            float a2bX = a.position().x - b.position().x;
            float a2bY = a.position().y - b.position().y;
            float a2bZ = a.position().z - b.position().z;

            float a2bDistance = (float) Math.sqrt(a2bX * a2bX + a2bY * a2bY + a2bZ * a2bZ);

            if (a2bDistance == 0) {
                a2bX = 0;
                a2bY = 0;
                a2bZ = 0;
            } else {
                a2bX /= a2bDistance;
                a2bY /= a2bDistance;
                a2bZ /= a2bDistance;
            }


            // spring force is proportional to how much it stretched

            float springForce = -(a2bDistance - restLength) * springConstant;


            // want velocity along line b/w a & b, damping force is proportional to this

            float Va2bX = a.velocity().x - b.velocity().x;
            float Va2bY = a.velocity().y - b.velocity().y;
            float Va2bZ = a.velocity().z - b.velocity().z;

            float dampingForce = -damping * (a2bX * Va2bX + a2bY * Va2bY + a2bZ * Va2bZ);


            // forceB is same as forceA in opposite direction

            float r = springForce + dampingForce;

            a2bX *= r;
            a2bY *= r;
            a2bZ *= r;

            final float MIN_FORCE = 0.0001f;
            if (Math.abs(a2bX) < MIN_FORCE &&
                Math.abs(a2bY) < MIN_FORCE &&
                Math.abs(a2bZ) < MIN_FORCE) {
                return;
            }

            if (a.isFree()) {
                a.force().add(a2bX, a2bY, a2bZ);
                a.setSpringInfluenced(true);
            }
            if (b.isFree()) {
                b.force().add(-a2bX, -a2bY, -a2bZ);
                b.setSpringInfluenced(true);
            }
        }
    }

    public boolean contains(Particle p) {
        return p == a || p == b;
    }

    public final float currentLength() {
        return a.position().distanceTo(b.position());
    }

    public final float damping() {
        return damping;
    }

    public final Particle getOneEnd() {
        return a;
    }

    public final Particle getTheOtherEnd() {
        return b;
    }

	public final boolean isOff() {
        return !on;
    }

	public final boolean isOn() {
        return on;
    }

    public final float restLength() {
        return restLength;
    }

    protected void setA(Particle p) {
        a = p;
    }

    protected void setB(Particle p) {
        b = p;
    }

    public final void setDamping(float d) {
        damping = d;
    }

    public final void setRestLength(float l) {
        restLength = l;
    }

    public final void setStrength(float ks) {
        springConstant = ks;
    }

    public final float strength() {
        return springConstant;
    }

	public final void turnOff() {
        on = false;
    }

  	public final void turnOn() {
        on = true;
    }
}
